/*                          UX-Framework                               
 *                                                                 
 *  iZE Co.,Ltd. Copyright 2009 All rights reserved.          
 *  This file is distributed under the terms of the GNU Lesser 
 *  General Public License, described in file LICENSE.                                     
 */

package jp.ize.ux.acts;

// {{{ imports
import jp.ize.ux.UXObject;
import jp.ize.ux.geom.UXPoint;
import jp.ize.ux.acts.Act;
import jp.ize.ux.utils.Util;
import jp.ize.ux.tween.UXTweenManager;
import jp.ize.ux.tween.UXTween;
// }}}
// {{{ typedef RingStateType
/**
 * RingActが必要な関数、プロパティの定義です。
 */
typedef RingStateType = {
	> ListStateType,
	/** 中心座標 */
	var center(default, default) : UXPoint;
	/** 半径 */
	var radius(default, default) : Float;
	/** 回転具合 */
	var theta(default, default) : Float;
}
// }}}
// {{{ typedef RingRotateParameter > ActParameter
/**
 * 回転パラメータを表現するクラス。
 */
typedef RingRotateParameter =
{
	> ActParameter,
	// {{{ var angle : Null<Int>;
	/**
	 * 回転量を角度で表現した数値です。
	 * 正の値は時計回り、負の値は反時計回りを意味します。
	 */
	var angle : Null<Int>;
	// }}}
	// {{{ var omega : Null<Float>;
	/**
	 * 回転量を示す数値です。
	 */
	var omega : Null<Float>;
	// }}}
}
// }}}
// {{{ class RingAct extends Act
/**
 * RingAct クラス<br>
 * 円上に配置された動きを表現するクラス。中心座標を持つ状態を仮定します。<br>
 *
 * @author		iZE Co., Ltd.<br>
 * @version		$Id$<br>
 */
class RingAct extends Act
{
	// {{{ public function new()
	/**
	 * コンストラクタ。
	 */
	public function new()
	{
		super();
	}
	// }}}
	// {{{ public function rotate( rp:RingRotateParameter, ?interp:RingStateType->RingRotateParameter ) : RingStateType -> Void
	/**
	 * 子コンポーネントを回転させるアクションです。
	 */
	public function rotate( rp:RingRotateParameter, ?interp:RingStateType->RingRotateParameter ) : RingStateType -> Void
	{
		var me = this;
		return function( state : RingStateType ) {
			var param = rp;
			if ( interp!=null ) param = interp(state);
			// me.state.touch();
			// . apply listener( START ).
			// . make some tweener.
			// . apply tweener over children.
			// . on tweener finish,
			//     . me.state.detouch();
			//     . notify listener( COMPLETE ).

			// just example.
			if ( param.omega==null ) param.omega = param.angle*Math.PI/180;
			var goal = state.theta + param.omega;
			var getter = function(obj:RingStateType): {v:Float} {
				return {v:obj.theta};
			};
			var setter = function(obj:RingStateType, init:{v:Float}, end:{v:Float}, v:Float) {
				obj.theta = init.v+(end.v-init.v)*v;
				_layout(obj);
			};
			var end = {v:goal};
			var tw = new UXTween<RingStateType,{v:Float}>(state, end, getter, setter, param.time, param.transition);
			tw.setEndTweenHandler( function(e:Dynamic){
				if ( state.subject!=null ) {
					state.subject.notify(Act.STOP);
				}
			});
			var id = "theta";
			if ( !me.tm.objects.exists(id) ) {
				me.tm.registObject(id,state);
			}
			me.tm.addTweenChain(id,tw); 
			me.tm.start(id,false,state.target);
		};
	}
	// }}}
	// {{{ public function layout( ap:ActParameter, ?interp:RingStateType->ActParameter ) : RingStateType -> Void
	/**
	 * 子コンポーネントをリング状に配置するアクションです。
	 */
	public function layout( ap:ActParameter, ?interp:RingStateType->ActParameter ) : RingStateType -> Void
	{
		var me = this;
		return function( state : RingStateType ) {
			me.initSync(state.children.length);
			me.setSyncDoneHandler( function(){
				if ( state.subject!=null ) {
					state.subject.notify(Act.STOP);
				}
			});
			var param = ap;
			if ( interp!=null ) param = interp(state);
			for (i in 0...state.children.length) {
				var c = state.children[i];
				var goal_x = state.center.x + state.radius * Math.cos(state.theta + i*2*Math.PI / state.children.length);
				var goal_y = state.center.y - state.radius * Math.sin(state.theta + i*2*Math.PI / state.children.length);
				var end = {x:goal_x,y:goal_y};
				var getter = function(c:UXObject):{x:Float,y:Float}{
					return {x:c.x, y:c.y};
				};
				var setter = function(c:UXObject,init:{x:Float,y:Float},end:{x:Float,y:Float},v:Float) {
					c.x = init.x+(end.x-init.x)*v;
					c.y = init.y+(end.y-init.y)*v;
				};
				var tw = new UXTween<UXObject,{x:Float,y:Float}>(c, end, getter, setter, param.time, param.transition);
				tw.setEndTweenHandler( function(e:Dynamic){
					me.doneSync(i);
				});
				var id:String = "xy" + Std.string(i);
				if ( !me.tm.objects.exists(id) ) {
					me.tm.registObject(id,c);
				}
				me.tm.addTweenChain(id,tw);
				me.tm.start(id,false,state.target);
			}
		};
	}
	// }}}
	#if flash10
	// {{{ public function rotateX( rp:RingRotateParameter, ?interp:RingStateType->RingRotateParameter ) : RingStateType -> Void
	/**
	 * 子コンポーネントをX軸方向に回転させるアクションです。（Flash Player 10以上）
	 */
	public function rotateX( rp:RingRotateParameter, ?interp:RingStateType->RingRotateParameter ) : RingStateType -> Void
	{
		var me = this;
		return function( state : RingStateType ) {
			var param = rp;
			if ( interp!=null ) interp(state);
			var p : UXObject = cast(state.children[0].parent);
			var end:{v:Float} = {v:p.rotationX + param.angle};
			var getter = function(p:UXObject):{v:Float}{
				return {v:p.rotationX};
			};
			var setter = function(p:UXObject,i:{v:Float},e:{v:Float},v:Float){
				var a:Float = i.v+(e.v-i.v)*v;
				p.rotationX = a;
				for (i in 0...state.children.length) {
					var c = state.children[i];
					c.rotationX = -a;
				}
			};
			var tw = new UXTween<UXObject,{v:Float}>(
						p, end, getter, setter, param.time, param.transition);
			tw.setEndTweenHandler( function(e:Dynamic){
				if ( state.subject!=null ) {
					state.subject.notify(Act.STOP);
				}
			});
			var id = "rotX";
			if ( !me.tm.objects.exists(id) ) {
				me.tm.registObject(id,state);
			}
			me.tm.addTweenChain(id,tw); 
			me.tm.start(id,false,state.target);
		}
	}
	// }}}
	#end
	// {{{ private static function _layout( state : RingStateType ) : Void
	/**
	 * 子コンポーネントを子コンポーネントのリサイズポリシーに従って円上に配置します。
	 */
	private static function _layout( state : RingStateType ) : Void
	{
		for (i in 0...state.children.length) {
			var c = state.children[i];
			c.x = state.center.x + state.radius
				* Math.cos(state.theta + i*2*Math.PI / state.children.length);
			c.y = state.center.y - state.radius
				* Math.sin(state.theta + i*2*Math.PI / state.children.length);
		}
		retouchZ(state);
	}
	// }}}
	// {{{ private static function retouchZ( state : RingStateType ) : Void
	/**
	 * 子要素の前後関係を整理しなおします
	 */
	private static function retouchZ( state : RingStateType ) : Void
	{
		#if flash10
		//重なり具合を調節する。
		// z_of : UXObject -> Float グローバルなz座標を求める関数
		var z_of : UXObject -> Float = function (c) {
 			var v = new flash.geom.Vector3D(c.x, c.y, c.z);
			var p : flash.display.Sprite = cast(c.parent);
			if (p.transform.matrix3D != null) {
				return p.transform.matrix3D.transformVector(v).z;
			}
			return 0;
		}
		var comp : UXObject -> UXObject -> Int = function(c1, c2)
			return Math.round(z_of(c2) - z_of(c1));
		var cs = state.children.copy();
		cs.sort(comp);
		for (i in 0...cs.length) {
			var child = cs[i];
			child.parent.setChildIndex(child, i);
		}
		#end
	}
	
	// }}}
}
// }}}
